/*
 * 文 件 名:  ConnectionPool.java
 * 版    权:  Huawei Technologies Co., Ltd. Copyright YYYY-YYYY,  All rights reserved
 * 描    述:  <描述>
 * 修 改 人:  admin
 * 修改时间:  2013-6-22
 * 跟踪单号:  <跟踪单号>
 * 修改单号:  <修改单号>
 * 修改内容:  <修改内容>
 */
package org.gfz.frame.core.database;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Properties;
import java.util.Vector;

/**
 * <一句话功能简述>连接池 <功能详细描述>
 * 
 * @author 姓名 工号
 * @version [版本号, 2013-6-22]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class ConnectionPool
{
    /**
     * 实例
     */
    private static ConnectionPool instance;
    
    /**
     * 连接集合
     */
    private Vector<Connection> connList;
    
    /**
     * 默认大小
     */
    private static final int DEFAULT_POOL_SIZE = 5;
    
    /**
     * 默认超时时间
     */
    private static final int DEFAULT_CONN_TIME_OUT = 40000;
    
    /**
     * 当某个连接在池中存在大于时间
     */
    private static long CHECK_CLOSED_CONNECTION_TIME = 4 * 60 * 60 * 1000;
    
    /**
     * 当连接使用时间大于30分钟时强制释放
     */
    private static long RELEASE_CONN_TIME = 30 * 60 * 1000;
    
    /**
     * 池最大大小
     */
    private int poolMaxSize;
    
    /**
     * <一句话功能简述> <功能详细描述>
     * 
     * @return
     * @see [类、类#方法、类#成员]
     */
    public static synchronized ConnectionPool getInstance()
    {
        if (null == instance)
        {
            instance = new ConnectionPool();
        }
        
        return instance;
    }
    
    /**
     * <一句话功能简述>获取连接 <功能详细描述> 如果连接池中存在此URL的连接，且空闲，可用，则从连接池中获取，如果连接池， 饱和且无此URL的可用连接，删除连接池中部分空闲连接，创建新URL连接，返回
     * 
     * @param url
     * @param userProp
     * @return
     * @throws SQLException
     * @see [类、类#方法、类#成员]
     */
    protected synchronized Connection getDBconnection(String url, Properties userProp)
        throws SQLException
    {
        DBConnection conn = null;
        System.out.println("enter  getDBconnection");
        
        // 检测线程池大小，如果无可用连接，且大小为最大大小，则移除一些无用连接
        checkPoolSize(url);
        
        // 在线程池中获取连接
        for (Connection temp : connList)
        {
            if (null != temp)
            {
                conn = (DBConnection)temp;
                
                if (!conn.isBusy() && conn.getUrl().equalsIgnoreCase(url) && conn.isValid())
                {
                    System.out.println("连接池存在空闲连接");
                    // 设置忙碌状态
                    conn.setBusy(true);
                    break;
                }
                else
                {
                    conn = null;
                }
            }
        }
        
        // 如果在池中获取失败，则创建新连接
        if (null == conn)
        {
            System.out.println("new connection");
            conn = newConnection(url, userProp);
            
            if (null != conn)
            {
                conn.setBusy(true);
                
                // 添加新连接到连接池
                connList.add(conn);
                
                System.out.println("add list size=" + connList.size());
            }
            
        }
        
        return conn;
    }
    
    /**
     * <默认构造函数>
     */
    private ConnectionPool()
    {
        poolMaxSize = DEFAULT_POOL_SIZE;
        connList = new Vector<Connection>(poolMaxSize);
        
        // 启动监控线程
        new MonitorPoolThread("POOL_Monitor").start();
    }
    
    /**
     * <一句话功能简述>检查线程池大小 <功能详细描述>
     * 
     * @param url
     * @throws SQLException
     * @see [类、类#方法、类#成员]
     */
    private void checkPoolSize(String url)
        throws SQLException
    {
        System.out.println("start checkPoolSize");
        
        long waitTime = 0;
        
        while (true)
        {
            if (connList.size() < poolMaxSize)
            {
                System.out.println("end checkPoolSize size = " + connList.size());
                break;
            }
            
            try
            {
                waitTime += DEFAULT_CONN_TIME_OUT;
                wait(DEFAULT_CONN_TIME_OUT);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            
            // 如果无当前URL空闲的连接，则需要移除线程池中一些无用的连接
            if (isFreeConn(url))
            {
                System.out.println("end checkPoolSize is free ");
                break;
            }
            else
            {
                if (waitTime >= DEFAULT_CONN_TIME_OUT)
                {
                    throw new SQLException("pool is max size and is not free conn ");
                }
            }
        }
        
    }
    
    /**
     * <一句话功能简述>查看连接池中是否存在当前URL的空闲连接 <功能详细描述>
     * 
     * @param url
     * @return
     * @see [类、类#方法、类#成员]
     */
    private synchronized boolean isFreeConn(String url)
    {
        boolean isFree = false;
        DBConnection conn = null;
        
        for (Connection temp : connList)
        {
            conn = (DBConnection)temp;
            
            if (!conn.isBusy() && conn.getUrl().equalsIgnoreCase(url) && conn.isValid())
            {
                System.out.println("conn is free");
                isFree = true;
                break;
            }
        }
        
        return isFree;
    }
    
    /**
     * <一句话功能简述>释放连接池中超时的连接 <功能详细描述>
     * 
     * @see [类、类#方法、类#成员]
     */
    protected synchronized void clearPool()
    {
        DBConnection conn = null;
        
        System.out.println("connList=" + connList.size());
        
        for (Connection temp : connList)
        {
            conn = (DBConnection)temp;
            
            if (isRelease(conn))
            {
                releaseConn(conn);
            }
        }
    }
    
    /**
     * <一句话功能简述>从线程池中移除连接 <功能详细描述>
     * 
     * @see [类、类#方法、类#成员]
     */
    protected synchronized void removeConn()
    {
        DBConnection conn = null;
        Iterator<Connection> connItr = connList.iterator();
        
        while (connItr.hasNext())
        {
            conn = (DBConnection)connItr.next();
            
            if (isRemove(conn))
            {
                try
                {
                    conn.closeConn();
                }
                catch (SQLException e)
                {
                    conn = null;
                }
                
                connItr.remove();
            }
        }
    }
    
    /**
     * <一句话功能简述>释放连接，设置连接对象为空闲 <功能详细描述>
     * 
     * @param conn
     * @see [类、类#方法、类#成员]
     */
    protected synchronized void releaseConn(DBConnection conn)
    {
        if (null == conn)
        {
            return;
        }
        
        // 设置连接对象为空闲
        conn.setBusy(false);
        
        System.out.println("连接被释放掉。。。。。。");
    }
    
    /**
     * <一句话功能简述>判断当前连接释放可以被释放 <功能详细描述>
     * 
     * @param conn
     * @return
     * @see [类、类#方法、类#成员]
     */
    private boolean isRelease(DBConnection conn)
    {
        boolean isRelease = false;
        final long useTime = System.currentTimeMillis() - conn.getLastUseTime();
        
        // 当连接处于忙碌，且使用时间大于超时时间时，释放连接
        if (conn.isBusy() && useTime > RELEASE_CONN_TIME)
        {
            isRelease = true;
        }
        
        System.out.println("当前连接释放可以释放。。。。" + isRelease);
        
        return isRelease;
    }
    
    /**
     * <一句话功能简述>判断释放可以移除 <功能详细描述>
     * 
     * @param conn 连接
     * @return
     * @see [类、类#方法、类#成员]
     */
    private boolean isRemove(DBConnection conn)
    {
        boolean isRemove = false;
        
        final long useTime = System.currentTimeMillis() - conn.getLastUseTime();
        
        // 当连接处于不忙状态或者最后使用时间在4小时前则可以删除连接
        if (!conn.isBusy() && useTime > CHECK_CLOSED_CONNECTION_TIME)
        {
            System.out.println("连接可以被移除了");
            isRemove = true;
        }
        
        System.out.println("当前连接释放可以删除。。。。" + isRemove);
        return isRemove;
    }
    
    /**
     * <一句话功能简述>创建新连接 <功能详细描述>
     * 
     * @param url 连接串
     * @param userProp 参数
     * @return Connection 连接对象
     * @see [类、类#方法、类#成员]
     */
    private synchronized DBConnection newConnection(String url, Properties userProp)
    {
        DBConnection dbConn = null;
        Connection conn = null;
        int newCount = 3;
        
        // 实例化创建连接线程
        newConnectionThread newConnThread = new newConnectionThread(url, userProp);
        
        for (int i = 0; i < newCount; i++)
        {
            System.out.println("开始创建新连接。。。。。 次数=" + (i + 1));
            
            // 启动线程
            new Thread(newConnThread).start();
            
            try
            {
                wait(10000);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
            
            conn = newConnThread.getConn();
            
            System.out.println("创建新连接结束。。。。。 次数=" + (i + 1));
            
            if (null != conn)
            {
                break;
            }
        }
        
        // 如果连接不为空，创建装饰连接
        if (null != conn)
        {
            dbConn = new DBConnection(this, url, conn);
        }
        
        return dbConn;
    }
    
    /**
     * <一句话功能简述>连接池健康健康线程 <功能详细描述>负责维护连接对象的使用状态，在超时情况下释放连接
     * 
     * @author 姓名 工号
     * @version [版本号, 2013-6-22]
     * @see [相关类/方法]
     * @since [产品/模块版本]
     */
    class MonitorPoolThread extends Thread
    {
        /**
         * <默认构造函数>
         */
        protected MonitorPoolThread(String name)
        {
            super(name);
        }
        
        @Override
        public void run()
        {
            while (true)
            {
                System.out.println("健康线程开始执行。。。");
                try
                {
                    Thread.sleep(DEFAULT_CONN_TIME_OUT);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                
                clearPool();
                removeConn();
                
                System.out.println("健康线程结束。。。");
            }
        }
    }
    
    /**
     * <一句话功能简述>创建连接线程 <功能详细描述>
     * 
     * @author 姓名 工号
     * @version [版本号, 2013-6-22]
     * @see [相关类/方法]
     * @since [产品/模块版本]
     */
    class newConnectionThread implements Runnable
    {
        /**
         * 连接对象
         */
        private Connection conn;
        
        /**
         * url
         */
        private String url;
        
        /**
         * 参数
         */
        private Properties userProp;
        
        /**
         * <默认构造函数>
         */
        protected newConnectionThread(String url, Properties userProp)
        {
            this.url = url;
            this.userProp = userProp;
            
        }
        
        @Override
        public void run()
        {
            DriverManager.setLoginTimeout(10);
            
            try
            {
                conn = DriverManager.getConnection(url, userProp);
            }
            catch (SQLException e)
            {
                System.out.println(e.getMessage());
            }
        }
        
        /**
         * <一句话功能简述>获取产生的连接 <功能详细描述>
         * 
         * @return Connection
         * @see [类、类#方法、类#成员]
         */
        public Connection getConn()
        {
            return conn;
        }
        
    }
}
